From b3ee62abc3cc6b5ef67db20678ca112e1fa4270f Mon Sep 17 00:00:00 2001 From: robertl Date: Tue, 28 Jun 2005 15:21:35 +0000 Subject: [PATCH] Olaf Klein contributes Mapsource GDB reader. git-svn-id: http://gpsbabel.googlecode.com/svn/trunk@1241 f51c46e8-681c-474f-0cfe-069cfd0219fb --- gpsbabel/gdb.c | 751 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 751 insertions(+) create mode 100644 gpsbabel/gdb.c diff --git a/gpsbabel/gdb.c b/gpsbabel/gdb.c new file mode 100644 index 000000000..0f1f8a85f --- /dev/null +++ b/gpsbabel/gdb.c @@ -0,0 +1,751 @@ +/* + Garmin GPS Database Reader + + Copyright (C) 2005 Olaf Klein, o.b.klein@t-online.de + Mainly based on mapsource.c, + Copyright (C) 2005 Robert Lipe, robertlipe@usa.net + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA +*/ + +#include +#include +#include + +#include "defs.h" +#include "garmin_tables.h" +#include "jeeps/gpsmath.h" + +#define MYNAME "gdb" + +#define MPSNAMEBUFFERLEN 1024 +#define MPSNOTESBUFFERLEN 4096 +#define MPSDESCBUFFERLEN 4096 + +#define DEFAULTICONVALUE 18 +#define DEFAULTICONDESCR "Waypoint" + +#ifdef UTF8_SUPPORT +# define GDB_UTF8_ENABLED 1 +#else +# define GDB_UTF8_ENABLED 0 +#endif + +/* %%% local vars %%% */ + +FILE *fin; +static char *fin_name; +static int gdb_ver = 1; +static int gdb_debug = 0; +route_head *gdb_hidden; + +/* %%% 1-1 functions from mapsource, should by shared!!! %%% */ + +waypoint * +gdb_find_wpt_q_by_name(const queue *whichQueue, const char *name) +{ + queue *elem, *tmp; + waypoint *waypointp; + + QUEUE_FOR_EACH(whichQueue, elem, tmp) { + waypointp = (waypoint *) elem; + if (0 == strcmp(waypointp->shortname, name)) { + return waypointp; + } + } + return NULL; +} + +const char * +gdb_find_desc_from_icon_number(const int icon, garmin_formats_e garmin_format) +{ + icon_mapping_t *i; + + for (i = garmin_icon_table; i->icon; i++) { + switch (garmin_format) { + case MAPSOURCE: + if (icon == i->mpssymnum) + return i->icon; + break; + case PCX: + case GARMIN_SERIAL: + if (icon == i->pcxsymnum) + return i->icon; + break; + default: + fatal(MYNAME ": unknown garmin format"); + } + } + return DEFAULTICONDESCR; +} + +/* %%% local functions (read support) %%% */ + +char * +gdb_convert_name_buff(char *buff, size_t buffsize) +{ +#ifdef UTF8_SUPPORT + char *tmp = str_garmin_to_utf8(buff); + strncpy(buff, tmp, buffsize); + xfree(tmp); +#endif + return buff; +} + +#ifdef GDB_DEBUG +static void +gdb_print_buff(const char *buff, int count, const char *comment) +{ + int i; + printf(MYNAME ": dump of %s : ", comment); + for (i = 0; i < count; i++) + { + printf("%02x ", buff[i] & 0xFF); + } + printf("\n"); + fflush(stdout); +} +#endif + +waypoint * +gdb_create_rte_wpt(const char *name, double lat, double lon, double alt) +{ + waypoint *wpt; + + wpt = find_waypt_by_name(name); + if (wpt != NULL) wpt = waypt_dupe(wpt); + else + { + gdb_find_wpt_q_by_name((queue *)&gdb_hidden->waypoint_list, name); + if (wpt != NULL) wpt = waypt_dupe(wpt); + else + { + wpt = waypt_new(); + wpt->shortname = xstrdup(name); + wpt->latitude = lat; + wpt->longitude = lon; + wpt->altitude = alt; + wpt->depth = unknown_alt; + } + } + return wpt; +} + + +static unsigned int +gdb_fread_str(FILE *fin, char *dest, size_t maxlen) +{ + int c; + unsigned int res = 0; + + while (maxlen-- > 0) + { + c = fgetc(fin); + if ( c != EOF ) + { + *dest++ = c; + if ( c == 0 ) return res; + res++; + } + else + { + *dest++ = '\0'; + return res; + } + } + fatal(MYNAME ": local buffer overflow detected, please report!\n"); +} + +static int +gdb_fread_le(FILE *fin, void *dest, size_t size, unsigned int bit_count, const char *field) +{ + char buff[32]; + unsigned char *c = dest; + short *sh = dest; + int *li = dest; + double *db = dest; + + if ((bit_count >> 3) != size) + fatal(MYNAME ": internal error (gdb_le_read/%d/%d/%s)!\n", size, bit_count >> 3, field); + + switch(bit_count) + { + case 8: + fread(c, sizeof(*c), 1, fin); + if (gdb_debug) printf(MYNAME ": gdb_fread_le : %d -> %s (0x%x))\n", *c, field, *c); + return *c; + case 16: + if (sizeof(*sh) != size) fatal(MYNAME ": internal decl.!\n"); + fread(sh, sizeof(*sh), 1, fin); + *sh = le_read16(sh); + if (gdb_debug) printf(MYNAME ": gdb_fread_le : %d -> %s (0x%x))\n", *sh, field, *sh); + return *sh; + case 32: + fread(li, 4, 1, fin); + *li = le_read32(li); + if (gdb_debug) printf(MYNAME ": gdb_fread_le : %d -> %s (0x%x)\n", *li, field, *li); + return *li; + case 64: + fread(buff, sizeof(*db), 1, fin); + le_read64(db, buff); + if (gdb_debug) printf(MYNAME ": gdb_fread_le : %g -> %s\n", *db, field); + return 0; + default: + fatal(MYNAME ": unsupported bit count (%d) in gdb_le_read!\n", bit_count); + } +} + +static void +gdb_is_valid(int is, const char *comment) +{ + if (is == 0) fatal(MYNAME ": error in database structure (%s)!\n", comment); +} + +static void +gdb_read_file_header(void) +{ + char buff[128]; + int i; + unsigned int reclen; + + i = gdb_fread_str(fin, buff, sizeof(buff)); + if ( i != 5 ) + fatal(MYNAME ": invalid database!\n"); + + if (strcmp(buff, "MsRcf") != 0) + fatal(MYNAME ": invalid database!\n"); + + fread(&reclen, 4, 1, fin); + reclen = le_read32(&reclen); + + gdb_is_valid(reclen == gdb_fread_str(fin, buff, sizeof(buff)), "header"); + + gdb_is_valid(buff[0] == 'D', "header"); + switch(buff[1]) + { + case 'k': + gdb_ver = 1; + break; + case 'l': + gdb_ver = 2; + break; + default: + fatal(MYNAME ": non supported gdb version!\n"); + } + + if (global_opts.verbose_status > 0) + printf(MYNAME ": found Garmin GPS Database version %d\n", gdb_ver); + + fread(&reclen, 4, 1, fin); + reclen = le_read32(&reclen); + gdb_is_valid(reclen < sizeof(buff), "header"); + fread(buff, reclen, 1, fin); + + gdb_is_valid(0 == gdb_fread_str(fin, buff, sizeof(buff)), "header"); + + i = gdb_fread_str(fin, buff, sizeof(buff)); + gdb_is_valid((i == 9) && (strcmp(buff, "MapSource") == 0), "header"); +} + +waypoint * +gdb_read_wpt(const size_t fileofs, int *wptclass) +{ + char xname[MPSNAMEBUFFERLEN]; + char xdesc[MPSDESCBUFFERLEN]; + char xnotes[MPSNOTESBUFFERLEN]; + int xclass; + int xlat, xlon, xdisplay, xcolour, xicon, xtime; + short xcat; + double xdepth = unknown_alt; + double xalt = unknown_alt; + + char buff[128]; + + size_t pos, delta; + + +/********************************************************************************************************/ +/* record structure + + zstring name + dword class + zstring country + 4 * 0x00 subclass part 1 + 12 * 0xFF subclass part 2 + 2 * 0x00 subclass part 3 + 4 * 0xFF unknown + dword latitude + dword longitude + if (1) +8 altitude = (1 or 9) + zstring comment + dword display flag + dword display colour + dword icon + zstring city ? + zstring state ? + zstring facility ? + char unknown ? + double depth (if flag) + zstring url + word category -> offset 79 + double temp (if flag) + */ +/********************************************************************************************************/ + + gdb_is_valid(gdb_fread_str(fin, xname, sizeof(xname)) > 0, "new waypoint"); + gdb_convert_name_buff(xname, sizeof(xname)); + + gdb_fread_le(fin, &xclass, sizeof(xclass), 32, "xclass"); + gdb_fread_str(fin, buff, sizeof(buff)); /* country */ + + fread(buff, 22, 1, fin); + xlat = gdb_fread_le(fin, &xlat, sizeof(xlat), 32, "xlat"); /* latitude */ + xlon = gdb_fread_le(fin, &xlon, sizeof(xlon), 32, "xlon"); /* latitude */ + + fread(buff, 1, 1, fin); + if (buff[0] == 1) /* altitude flag */ + { + gdb_fread_le(fin, &xalt, sizeof(xalt), 64, "xalt"); /* altitude */ + } + + gdb_fread_str(fin, xdesc, sizeof(xdesc)); + gdb_convert_name_buff(xdesc, sizeof(xdesc)); + + fread(buff, 1, 1, fin); /* proximity flag */ + if (buff[0] == 1) + { + fread(buff, 8, 1, fin); /* proximity */ + } + + xdisplay = gdb_fread_le(fin, &xdisplay, sizeof(xdisplay), 32, "xdisplay"); + xcolour = gdb_fread_le(fin, &xcolour, sizeof(xcolour), 32, "xcolour"); + xicon = gdb_fread_le(fin, &xicon, sizeof(xicon), 32, "xicon"); + + /* ToDo: convert the icon !!! */ + + gdb_fread_str(fin, buff, sizeof(buff)); /* city */ + gdb_fread_str(fin, buff, sizeof(buff)); /* state */ + gdb_fread_str(fin, buff, sizeof(buff)); /* facility */ + fread(buff, 1, 1, fin); /* unknown */ + + fread(buff, 1, 1, fin); /* depth flag */ + if (buff[0] == 1) + { + gdb_fread_le(fin, &xdepth, sizeof(xdepth), 64, "xdepth"); /* depth */ + } + + fread(buff, 1, 1, fin); + fread(buff, 1, 1, fin); + fread(buff, 1, 1, fin); + + if (buff[0] != 0) + fread(buff, 3, 1, fin); + else + fread(buff, 4, 1, fin); + + gdb_fread_str(fin, xnotes, sizeof(xnotes)); + gdb_convert_name_buff(xnotes, sizeof(xnotes)); + + xcat = gdb_fread_le(fin, &xcat, sizeof(xcat), 16, "xcat"); + + fread(buff, 1, 1, fin); /* temperature flag */ + if (buff[0] == 1) + { + fread(buff, 8, 1, fin); /* temperature */ + } + + /* Here comes 1 .. 6 unknown bytes + !!! 6 only if class > 0 !!! + the field seems to be a time stamp */ + + pos = ftell(fin); + delta = fileofs - pos; + gdb_is_valid(delta > 0, "waypoint final"); + + xtime = 0; + if (xclass == 0) + { + fread(buff, 1, 1, fin); + if (buff[0] == 1) + { + gdb_is_valid(delta==5, "??? waypoint time ???"); + gdb_fread_le(fin, &xtime, sizeof(xtime), 32, "xtime"); + } + else + gdb_is_valid(delta==1, "no waypoint time"); + } + + *wptclass = xclass; + + waypoint *res = waypt_new(); + res->shortname = xstrdup(xname); + res->description = xstrdup(xdesc); + res->notes = xstrdup(xnotes); + res->latitude = GPS_Math_Semi_To_Deg(xlat); + res->longitude = GPS_Math_Semi_To_Deg(xlon); + res->altitude = xalt; + res->creation_time = xtime; + /* might need to change this to handle version dependent icon handling */ + res->icon_descr = gdb_find_desc_from_icon_number(xicon, MAPSOURCE); + + return res; +} + +static void +gdb_read_route(void) +{ + char xname[MPSNAMEBUFFERLEN]; + char xwptname[MPSNAMEBUFFERLEN]; + int xclass; + double xalt; + double xlat = 0.0, xlon = 0.0; + + char buff[256]; + int count; + int isteps, ilink; + int semilat, semilon; + + route_head *route; + waypoint *wpt; + + unsigned int i, j; + size_t curpos; + + gdb_is_valid(gdb_fread_str(fin, xname, sizeof(xname)) > 0, "route start"); + gdb_convert_name_buff(xname, sizeof(xname)); + + gdb_fread_le(fin, buff, 2, 16, "auto_name"); + gdb_fread_le(fin, buff, 4, 32, "max_lat"); + gdb_fread_le(fin, buff, 4, 32, "max_lon"); + + fread(buff, 1, 1, fin); + if (buff[0] == 1) gdb_fread_le(fin, buff, 8, 64, "max_alt"); + + gdb_fread_le(fin, buff, 4, 32, "min_lat"); + gdb_fread_le(fin, buff, 4, 32, "min_lon"); + + fread(buff, 1, 1, fin); + if (buff[0] == 1) gdb_fread_le(fin, buff, 8, 64, "min_alt"); + + gdb_fread_le(fin, &count, sizeof(count), 32, "rte_count"); + if (count <= 0) return; + + route = route_head_alloc(); + route->rte_name = xstrdup(xname); + route_add_head(route); + +#if GDB_DEBUG + printf(MYNAME " - route: \"%s\" with %d point(s)\n", route->rte_name, count); +#endif + + if (count <= 0) return; + + count--; + + while (count--) + { + gdb_fread_str(fin, xwptname, sizeof(xwptname)); /* name */ + gdb_convert_name_buff(xwptname, sizeof(xwptname)); + + gdb_fread_le(fin, &xclass, sizeof(xclass), 32, "xclass"); /* class */ + gdb_fread_str(fin, buff, sizeof(buff)); /* country */ + + fread(buff, 22, 1, fin); /* sub class data */ + i = 0; + while (i < sizeof(buff)) + { + fread(&buff[i], 1, 1, fin); + if (buff[i] == 0) break; + i++; + } + + /* The next thing is the unknown 0x03 0x00 .. 0x00 (18 bytes) */ + fread(buff, 18, 1, fin); + + gdb_fread_le(fin, &isteps, sizeof(isteps), 32, "isteps"); + + if (isteps <= 0) return; + + gdb_fread_le(fin, &semilat, sizeof(semilat), 32, "semilat"); + gdb_fread_le(fin, &semilon, sizeof(semilon), 32, "semilon"); + xlat = GPS_Math_Semi_To_Deg(semilat); + xlon = GPS_Math_Semi_To_Deg(semilon); + + gdb_is_valid(fabs(xlat) < 180.0 && fabs(xlon) < 180.0, " - rte: read loop: invalid lat or lon"); + gdb_is_valid(fabs(xlat) > 1 && fabs(xlon) > 1, " - rte: read loop: invalid lat or lon"); + + xalt = unknown_alt; + fread(buff, 1, 1, fin); /* altitude flag */ + if (buff[0] == 1) + { + gdb_fread_le(fin, &xalt, sizeof(xalt), 64, "xalt"); + } + gdb_is_valid(xlat > 1 && xlon > 1, " - rte: read loop"); + + wpt = gdb_create_rte_wpt(xwptname, xlat, xlon, xalt); + route_add_wpt(route, wpt); + + for (ilink = isteps-1; ilink > 0; ilink--) + { + gdb_fread_le(fin, &semilat, sizeof(semilat), 32, "semilat"); + gdb_fread_le(fin, &semilon, sizeof(semilon), 32, "semilon"); + fread(buff, 1, 1, fin); /* altitude flag */ + if (buff[0] == 1) gdb_fread_le(fin, &xalt, sizeof(xalt), 64, "xalt"); + xlat = GPS_Math_Semi_To_Deg(semilat); + xlon = GPS_Math_Semi_To_Deg(semilon); + gdb_is_valid(xlat > 1 && xlon > 1, " - rte/ils: read loop: invalid lat or lon"); + } + + fread(buff, 1, 1, fin); /* NULL */ + gdb_is_valid(buff[0] == 0, "should be zero byte"); + + fread(buff, 4, 1, fin); /* link max lat */ + fread(buff, 4, 1, fin); /* link max lon */ + fread(buff, 1, 1, fin); + if (buff[0] == 1) + { + fread(buff, 8, 1, fin); /* link max alt validity + alt */ + } + fread(buff, 4, 1, fin); /* link min lat */ + fread(buff, 4, 1, fin); /* link min lon */ + + fread(buff, 1, 1, fin); + if (buff[0] == 1) + { + fread(buff, 8, 1, fin); /* link min alt validity + alt */ + } + + /* find the end of the record */ + + curpos = ftell(fin); + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + the stuff here is very tricky and did not base on any + any knowledgement, but seems to work. + The final structure varied from file to file and i + could not find any connection with data, gdb version + and any unknown bytes and bits. Hmm. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + memset(buff, 0, sizeof(buff)); + int checked = 0; + + j = 18; + while (checked == 0 && j-- > 0) + { + for (i=1; i<8; i++) buff[i-1] = buff[i]; + fread(&buff[7], 1, 1, fin); + for (i=0; i<8; i++) + { + if (buff[i] != -1) break; + if (i == 7) checked = 1; + } + + } + if (checked == 0) + { + fseek(fin, curpos, SEEK_SET); + } + else + { + while (1) + { + fread(buff, 1, 1, fin); + if (buff[0] != -1) + { + fseek(fin, ftell(fin)-1, SEEK_SET); + break; + } + } + + } + } + + gdb_fread_str(fin, xwptname, sizeof(xwptname)); /* name */ + gdb_convert_name_buff(xwptname, sizeof(xwptname)); + +#if GDB_DEBUG + printf(MYNAME " - rte/fin: \"%s\"\n", xwptname); +#endif + gdb_fread_le(fin, &xclass, sizeof(xclass), 32, "xclass"); /* class */ + gdb_fread_str(fin, buff, sizeof(buff)); /* country */ + + wpt = gdb_create_rte_wpt(xwptname, xlat, xlon, xalt); + route_add_wpt(route, wpt); + + return; +} + + +route_head * +gdb_read_track(const size_t max_file_pos) +{ + char xname[MPSNAMEBUFFERLEN]; + unsigned char xdisplay; + int xcolour; + int xlat; + int xlon; + int xtime = 0; + double xalt = unknown_alt; + double xdepth = unknown_alt; + double xtemp; + + char buff[128]; + int count; + + route_head *track; + waypoint *wpt; + + gdb_fread_str(fin, xname, sizeof(xname)); + gdb_convert_name_buff(xname, sizeof(xname)); + + gdb_fread_le(fin, &xdisplay, sizeof(xdisplay), 8, "xdisplay"); + gdb_fread_le(fin, &xcolour, sizeof(xcolour), 32, "xcolour"); + gdb_fread_le(fin, &count, sizeof(count), 32, "count"); + + track = route_head_alloc(); + track->rte_name = xstrdup(xname); + track_add_head(track); + + while (count > 0) + { + count--; + + gdb_fread_le(fin, &xlat, sizeof(xlat), 32, "xlat"); + gdb_fread_le(fin, &xlon, sizeof(xlon), 32, "xlon"); + + fread(buff, 1, 1, fin); /* altitude flag */ + if (buff[0] == 1) + gdb_fread_le(fin, &xalt, sizeof(xalt), 64, "xalt"); + + fread(buff, 1, 1, fin); /* date/time flag */ + if (buff[0] == 1) + gdb_fread_le(fin, &xtime, sizeof(xtime), 32, "xtime"); + + fread(buff, 1, 1, fin); /* depth flag */ + if (buff[0] == 1) + gdb_fread_le(fin, &xdepth, sizeof(xdepth), 64, "xdepth"); + + fread(buff, 1, 1, fin); + if (buff[0] == 1) + gdb_fread_le(fin, &xtemp, sizeof(xtemp), 64, "xtemp"); + +#if GDB_DEBUG + printf("trk: %g - %g (%08x - %08x)\n", + GPS_Math_Semi_To_Deg(xlat), GPS_Math_Semi_To_Deg(xlon), xlat, xlon); +#endif + + wpt = waypt_new(); + + wpt->latitude = GPS_Math_Semi_To_Deg(xlat); + wpt->longitude = GPS_Math_Semi_To_Deg(xlon); + wpt->creation_time = xtime; + wpt->centiseconds = 0; + wpt->altitude = xalt; + wpt->depth = xdepth; + + route_add_wpt(track, wpt); + } + + return track; +} + + +static void +gdb_read_data(void) +{ + int reclen, done; + char typ; + size_t curpos, deltapos; + waypoint *wpt; + int wptclass; + + done = 0; + while (!feof(fin) && (done == 0)) + { + + gdb_fread_le(fin, &reclen, sizeof(reclen), 32, "reclen"); + gdb_is_valid(reclen > 0 && reclen < 0x1F00000, "gdb data loop"); + fread(&typ, 1, 1, fin); + curpos = ftell(fin); + + switch(typ) + { + case 'W': + wpt = gdb_read_wpt(curpos + reclen, &wptclass); + if (wpt != NULL ) + { + if (wptclass == 0) + waypt_add(wpt); + else + route_add_wpt(gdb_hidden, wpt); + } + deltapos = (curpos+reclen)-ftell(fin); + break; + case 'T': + gdb_read_track(curpos + reclen); + break; + case 'R': + gdb_read_route(); + break; + case 'L': + break; + case 'V': + done = 1; + break; + default: + printf(MYNAME ": found unknown record type \"%c\"!\n", typ); + } + fseek(fin, curpos + reclen, SEEK_SET); + } +} + +/* %%% gobal callbacks %%% */ + +static void gdb_rd_init(const char *fname) +{ + fin_name = xstrdup(fname); + fin = xfopen(fname, "rb", MYNAME); + gdb_hidden = route_head_alloc(); + track_add_head(gdb_hidden); +} + +static void gdb_rd_deinit(void) +{ + track_del_head(gdb_hidden); + fclose(fin); + xfree(fin_name); +} + +static void gdb_read(void) +{ + gdb_read_file_header(); + gdb_read_data(); +} + + +ff_vecs_t gdb_vecs = { + ff_type_file, + { ff_cap_read, ff_cap_read, ff_cap_read }, /* FF_CAP_RW_ALL, */ + gdb_rd_init, + NULL, /* gdb_wr_init, */ + gdb_rd_deinit, + NULL, /* gdb_wr_deinit, */ + gdb_read, + NULL, /* gdb_write, */ + NULL, + NULL /* gdb_args */ +}; -- 2.30.2